//////////////////////////////////////////////////////////////////////////
#pragma once
#pragma managed
//////////////////////////////////////////////////////////////////////////
#include "ManagedImageConverter.h"
//////////////////////////////////////////////////////////////////////////
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Media;
using namespace System::Windows::Media::Imaging;
using namespace System::Windows::Controls;
using namespace System::Windows::Automation::Peers;
//////////////////////////////////////////////////////////////////////////
NAMESPACE_BEGIN_UI
//////////////////////////////////////////////////////////////////////////
public ref class ImageViewer : public FrameworkElement
{
public:
	static initonly DependencyProperty^ SourceProperty = DependencyProperty::Register("Source", ManagedArr::typeid, ImageViewer::typeid,
		gcnew FrameworkPropertyMetadata(nullptr, FrameworkPropertyMetadataOptions::AffectsRender | FrameworkPropertyMetadataOptions::AffectsMeasure, 
		gcnew PropertyChangedCallback(ImageViewer::OnSourceChanged), nullptr), nullptr);

	static initonly DependencyProperty^ StretchDirectionProperty = Viewbox::StretchDirectionProperty->AddOwner(ImageViewer::typeid);
	static initonly DependencyProperty^ StretchProperty = Viewbox::StretchProperty->AddOwner(ImageViewer::typeid);

	static ImageViewer()
	{
		System::Windows::Style^ defaultValue = CreateDefaultStyles();
		FrameworkElement::StyleProperty->OverrideMetadata(ImageViewer::typeid, gcnew FrameworkPropertyMetadata(defaultValue));
		StretchProperty->OverrideMetadata(ImageViewer::typeid, gcnew FrameworkPropertyMetadata(System::Windows::Media::Stretch::Uniform, FrameworkPropertyMetadataOptions::AffectsMeasure));
		StretchDirectionProperty->OverrideMetadata(ImageViewer::typeid, gcnew FrameworkPropertyMetadata(System::Windows::Controls::StretchDirection::Both, FrameworkPropertyMetadataOptions::AffectsMeasure));
	}

private:
	static System::Windows::Style^ CreateDefaultStyles()
	{
		System::Windows::Style^ style = gcnew System::Windows::Style(ImageViewer::typeid, nullptr);
		style->Setters->Add(gcnew Setter(FrameworkElement::FlowDirectionProperty, System::Windows::FlowDirection::LeftToRight));
		style->Seal();
		return style;
	}

	static void OnSourceChanged(DependencyObject^ d, DependencyPropertyChangedEventArgs e)
	{
		ImageViewer^ image = (ImageViewer^)d;
		ManagedArr^ oldValue = (ManagedArr^)e.OldValue;
		ManagedArr^ newValue = (ManagedArr^)e.NewValue;
		image->Source = newValue;
	}

public:
	ImageViewer(void);

public:
	property ManagedArr^ Source
	{
		ManagedArr^ get() { return (ManagedArr^)this->GetValue(SourceProperty); }
		void set(ManagedArr^ source) 
		{
			renderSource = ManagedImageConverter::Convert(source, renderSource, nullptr);
			this->SetValue(SourceProperty, source); 
		}
	}

	property System::Windows::Media::Stretch Stretch
	{
		System::Windows::Media::Stretch get() { return (System::Windows::Media::Stretch)this->GetValue(StretchProperty); }
		void set(System::Windows::Media::Stretch stretch) { this->SetValue(StretchProperty, stretch); }
	}

	property System::Windows::Controls::StretchDirection StretchDirection
	{
		System::Windows::Controls::StretchDirection get() { return (System::Windows::Controls::StretchDirection) this->GetValue(StretchDirectionProperty); }
		void set(System::Windows::Controls::StretchDirection direction) { this->SetValue(StretchDirectionProperty, direction); }
	}

protected:
	virtual Size ArrangeOverride(Size arrangeSize) override
	{
		return this->MeasureArrangeHelper(arrangeSize);
	}

	virtual Size MeasureOverride(Size constraint) override
	{
		return this->MeasureArrangeHelper(constraint);
	}

	virtual void OnRender(DrawingContext^ dc) override
	{
		WriteableBitmap^ source = this->renderSource;
		if (source != nullptr)
		{
			dc->DrawImage(source, System::Windows::Rect(Point(), this->RenderSize));
		}
	}

private:
	Size MeasureArrangeHelper(Size inputSize)
	{
		WriteableBitmap^ source = this->renderSource;
		Size contentSize;
		if (source == nullptr)
		{
			return contentSize;
		}
		contentSize = Size(source->Width, source->Height);
		Size size2 = ComputeScaleFactor(inputSize, contentSize, this->Stretch, this->StretchDirection);
		return Size(contentSize.Width * size2.Width, contentSize.Height * size2.Height);
	}

	static Size ComputeScaleFactor(Size availableSize, Size contentSize, System::Windows::Media::Stretch stretch, System::Windows::Controls::StretchDirection stretchDirection)
	{
		double width = 1.0;
		double height = 1.0;
		bool flag = !Double::IsPositiveInfinity(availableSize.Width);
		bool flag2 = !Double::IsPositiveInfinity(availableSize.Height);
		if ((((stretch != System::Windows::Media::Stretch::Uniform) && 
			(stretch != System::Windows::Media::Stretch::UniformToFill)) && 
			(stretch != System::Windows::Media::Stretch::Fill)) || (!flag && !flag2))
		{
			return Size(width, height);
		}
		else
		{
			width = Math::Abs(contentSize.Width) <= Double::Epsilon ? 0.0 : (availableSize.Width / contentSize.Width);
			height = Math::Abs(contentSize.Height) <= Double::Epsilon ? 0.0 : (availableSize.Height / contentSize.Height);
			if (!flag)
			{
				width = height;
			}
			else if (!flag2)
			{
				height = width;
			}
			else
			{
				switch (stretch)
				{
				case System::Windows::Media::Stretch::Fill:
					break;
				case System::Windows::Media::Stretch::Uniform:
					{
						double num3 = (width < height) ? width : height;
						width = height = num3;
						break;
					}
				case System::Windows::Media::Stretch::UniformToFill:
					{
						double num4 = (width > height) ? width : height;
						width = height = num4;
						break;
					}
				}
			}

			switch (stretchDirection)
			{
			case System::Windows::Controls::StretchDirection::UpOnly:
				if (width < 1.0)
				{
					width = 1.0;
				}
				if (height < 1.0)
				{
					height = 1.0;
				}
				break;
			case System::Windows::Controls::StretchDirection::DownOnly:
				if (width > 1.0)
				{
					width = 1.0;
				}
				if (height > 1.0)
				{
					height = 1.0;
				}
				break;

			case System::Windows::Controls::StretchDirection::Both:
				break;
			}
			return Size(width, height);
		}
	}

private:
	WriteableBitmap^ renderSource;
};
//////////////////////////////////////////////////////////////////////////
NAMESPACE_END_UI
//////////////////////////////////////////////////////////////////////////